use std::io::Write;


use wtask_base::{
    error::{WError, WResult},
    export::{AsyncBufReadExt, BufReader, TcpStream, log_debug, log_info, tokio_stdin, yield_now},
    machine::{CMDArgsConfigMachine, get_file_maching_running, random_token},
    tcp::{WTcpStream, stream_write_data, stream_write_raw, tcp_client_session_start}
};

use crate::{basic::{WRaw, WStreamReader}, utils::{App, AppRes, AppTrait}};




pub(super) fn write_log(data: Vec<u8>) {
    let _ = std::io::stdout().write_all(&data);
    let _ = std::io::stdout().flush();
}

pub fn msg_func_none(_err: bool, _msg: String) {
}



pub fn msg_func_print(err: bool, msg: String) {
    if err {
        eprintln!("{}", msg);
    } else {
        println!("{}", msg);
    }
}



async fn check_machine_running(config: &CMDArgsConfigMachine, session_token: &str) -> WResult<WTcpStream> {
    let (ip, port, server_token) = {
        (
            config.machine_ip.as_str(),
            config.machine_port,
            config.machine_token.as_str()
        )
    };
    if ip.eq("0.0.0.0") {
        let file_running = get_file_maching_running();
        if !file_running.exists() {
            return Err(WError::NetError("Local server not running!".to_string()));
        }
    }
    // log_debug!("cli session connected: {session_token}");
    let stream = tcp_client_session_start(
        ip,
        port,
        server_token,
        session_token
    ).await?;
    Ok(stream)
}


pub async fn cli_app_stream<T: Into<App>>(app: T, machine: CMDArgsConfigMachine) -> WResult<(WTcpStream, String)> {
    let session_token = random_token();
    let stream = check_machine_running(&machine, &session_token).await?;
    let data: App = app.into();
    stream_write_data(
        &data,
        stream.1.as_ref(),
        &session_token
    ).await?;
    Ok((stream, session_token))
}


pub async fn cli_app<T: AppTrait, M: FnMut(bool, String) + Send + Sync>(app: T, config: &CMDArgsConfigMachine, status: Option<T::Status>, mut msg_func: M) -> WResult<Option<T::Res>> {
    log_debug!("cli app start {app:?}");
    let (app_req, status) = app.client_handle_before(&mut msg_func, status)?;
    if app_req.is_none() {
        return Ok(None);
    }
    let session_token = random_token();
    let stream = check_machine_running(config, &session_token).await?;
    let app = app_req.unwrap();
    stream_write_data(
        &app.clone().into(),
        stream.1.as_ref(),
        &session_token
    ).await?;
    let app_res = app.client_handle(&stream, &session_token, msg_func, status).await?;
    log_debug!("machine cli over");
    Ok(Some(app_res))
}


pub async fn cli_appres<M: FnMut(bool, String) + Send + Sync>(app: App, config: &CMDArgsConfigMachine, msg_func: M) -> WResult<AppRes> {
    log_debug!("cli app start {app:?}");
    let session_token = random_token();
    let stream = check_machine_running(config, &session_token).await?;
    stream_write_data(
        &app,
        stream.1.as_ref(),
        &session_token
    ).await?;
    let app_res = app.client_handle(&stream, &session_token, msg_func).await?;
    log_debug!("machine cli over");
    let app_res = if let AppRes::Raw(WRaw::Raw(_)) = app_res {
        AppRes::Stream(WStreamReader {
            stream_read: stream.0,
            key: session_token
        })
    } else {
        app_res
    };
    Ok(app_res)
}





pub(super) async fn stdin_read_line(stream_write: &TcpStream, key: &str, prefix: &str) {
    let mut buf = String::new();
    log_info!("Waiting input ...");
    let mut reader = BufReader::new(tokio_stdin());
    while let Ok(n) = reader.read_line(&mut buf).await {
        if n == 0 {
            break;
        }
        log_debug!("shell input: {:?}", buf);
        let _ = stream_write_raw(stream_write, format!("{}{}", prefix, buf).as_bytes(), key).await;
        buf.clear();
        yield_now().await;
    }
}